import * as THREE from 'three'
import { MeshStandardMaterial } from 'three'
import createApp from '../../core'

function createMeshBasicMaterial (dom: HTMLElement) {
  const { camera, scene, gui, orbitControls } = createApp(dom)
  camera.position.set(-80, 90, 40)
  camera.lookAt(scene.position)

  const { color, intensity, distance, angle, penumbra, decay } = lightData
  const spotLight = new THREE.SpotLight(color, intensity, distance, angle, penumbra, decay)
  spotLight.position.set(-50, 70, -60)
  scene.add(spotLight)

  const spotLightHelper = new THREE.SpotLightHelper(spotLight, 0xff0000)
  scene.add(spotLightHelper)

  const axes = new THREE.AxesHelper(100)
  scene.add(axes)

  const planeGeometry = new THREE.PlaneGeometry(200, 200)
  const planeMaterial = new MeshStandardMaterial({ color: 0xffffff, side: THREE.DoubleSide })
  const plane = new THREE.Mesh(planeGeometry, planeMaterial)
  plane.rotateX(-Math.PI / 2)
  scene.add(plane)

  const geometry = new THREE.BoxGeometry(30, 30, 30)
  const material = new THREE.MeshBasicMaterial({ color: 0x7777ff })
  const mesh = new THREE.Mesh(geometry, material)
  mesh.position.set(0, 30, 0)
  scene.add(mesh)

  const box = new THREE.Box3()
  box.expandByObject(mesh)
  const center = new THREE.Vector3()
  box.getCenter(center)
  camera.lookAt(center)
  orbitControls.target.set(center.x, center.y, center.z)

  const loader = new THREE.TextureLoader()
  let baseTexture:THREE.Texture|null = null
  import('@/assets/material/tile.jpg').then(img => {
    loader.load(img.default, texture => {
      baseTexture = material.map = texture
      material.needsUpdate = true
    })
  })

  // 设置环境反射材质，注意天空盒6个材质必须是正方形的
  const cubeLoader = new THREE.CubeTextureLoader()
  let cubeTexture: THREE.CubeTexture|null = null
  getCubeImageUrl().then(urls => {
    cubeLoader.load(urls, texture => {
      cubeTexture = material.envMap = texture
      // 默认是反射，设置成折射参数，可以调整材质的折射率refractionRatio，来达到反射材质的远近效果
      cubeTexture.mapping = THREE.CubeRefractionMapping
      cubeTexture.format = THREE.RGBFormat
      scene.background = cubeTexture
      material.needsUpdate = true
    })
  })

  const folder1 = gui.addFolder('光源设置')
  folder1.addColor(lightData, 'color').onChange(() => {
    spotLight.color = new THREE.Color(lightData.color)
    spotLightHelper.update()
  })
  folder1.add(lightData, 'intensity', 0, 10).onChange(() => {
    spotLight.intensity = lightData.intensity
    spotLightHelper.update()
  })
  folder1.add(lightData, 'distance', 0, 500).onChange(() => {
    spotLight.distance = lightData.distance
    spotLightHelper.update()
  })
  folder1.add(lightData, 'angle', 0, Math.PI * 2).onChange(() => {
    spotLight.angle = lightData.angle
    spotLightHelper.update()
  })
  folder1.add(lightData, 'penumbra', 0, 10).onChange(() => {
    spotLight.penumbra = lightData.penumbra
    spotLightHelper.update()
  })
  folder1.add(lightData, 'decay', 0, 10).onChange(() => {
    spotLight.decay = lightData.decay
    spotLightHelper.update()
  })

  // opacity属性要生效 必须要把transparent设置为true,且opacity的值必须要大于alphaTest
  function updateMaterial () {
    const { color, visible, transparent, opacity, side, depthTest, depthWrite, alphaTest, isMap, mappingIsReflect, isEnvMap, reflectivity, refractionRatio } = materialData
    material.map = isMap
      ? baseTexture
      : null

    const cubeTextures = cubeTexture as THREE.CubeTexture
    cubeTextures.mapping = mappingIsReflect ? THREE.CubeReflectionMapping : THREE.CubeRefractionMapping
    material.envMap = isEnvMap ? cubeTexture : null
    material.color = new THREE.Color(color)
    material.visible = visible
    material.transparent = transparent
    material.opacity = opacity
    material.depthTest = depthTest
    material.depthWrite = depthWrite
    material.alphaTest = alphaTest
    switch (side) {
      case 'front':
        material.side = THREE.FrontSide
        break
      case 'back':
        material.side = THREE.BackSide
        break
      case 'double':
        material.side = THREE.DoubleSide
        break
    }
    material.reflectivity = reflectivity
    material.refractionRatio = refractionRatio
    material.needsUpdate = true
  }
  const folder2 = gui.addFolder('box材质设置')
  folder2.addColor(materialData, 'color').onChange(updateMaterial)
  folder2.add(materialData, 'visible').onChange(updateMaterial)
  folder2.add(materialData, 'transparent').onChange(updateMaterial)
  folder2.add(materialData, 'opacity', 0, 1).onChange(updateMaterial)
  folder2.add(materialData, 'side', ['front', 'back', 'double']).onChange(updateMaterial)
  folder2.add(materialData, 'depthTest').onChange(updateMaterial)
  folder2.add(materialData, 'depthWrite').onChange(updateMaterial)
  folder2.add(materialData, 'alphaTest', 0, 1).onChange(updateMaterial)
  folder2.add(materialData, 'isMap').onChange(updateMaterial)
  // 是否开启反射，默认开启，另一个值是折射
  folder2.add(materialData, 'mappingIsReflect').onChange(updateMaterial)
  folder2.add(materialData, 'isEnvMap').onChange(updateMaterial)
  // 反射率，默认为1,0表示不反射
  folder2.add(materialData, 'reflectivity', 0, 1).onChange(updateMaterial)
  // 折射率，要开启纹理的折射属性才能生效
  folder2.add(materialData, 'refractionRatio', 0, 1).onChange(updateMaterial)

  const folder3 = gui.addFolder('天空盒')
  folder3.add(sceneData, 'isMap').onChange(() => {
    scene.background = sceneData.isMap ? cubeTexture : null
  })
}

const lightData = {
  color: 0xff0000,
  intensity: 1,
  distance: 200,
  angle: Math.PI / 4,
  penumbra: 1,
  decay: 0
}

const materialData = {
  color: 0x7777ff,
  visible: true,
  transparent: false,
  opacity: 1,
  side: 'front',
  depthTest: true,
  depthWrite: true,
  alphaTest: 0,
  isMap: true,
  isEnvMap: true,
  mappingIsReflect: true,
  reflectivity: 1,
  refractionRatio: 0.98
}

const sceneData = {
  isMap: true
}

async function getCubeImageUrl () {
  const res = await Promise.all([
    import('@/assets/material/scene1.jpg'),
    import('@/assets/material/scene2.jpg'),
    import('@/assets/material/scene3.jpg'),
    import('@/assets/material/scene4.jpg'),
    import('@/assets/material/scene5.jpg'),
    import('@/assets/material/scene6.jpg')
  ])
  return res.map(data => data.default)
}

export {
  createMeshBasicMaterial
}
